import type { RedisClient } from "./client";

export interface FieldValue {
    field: string;
    value: string;
};

export class HashCommand {
    constructor(client: RedisClient) {
        this.client = client;
    }

    private readonly client: RedisClient;

    // return the number of fields that were removed from the hash
    async hdel(key: string, fieldList: string[]): Promise<number> {
        const res = await this.client.exec("HDEL", key, ...fieldList);
        return res as number;
    }

    // return 1 if the hash contains field,0 for not
    async hexists(key: string, field: string): Promise<number> {
        const res = await this.client.exec("HEXISTS", key, field);
        return res as number;
    }

    // return value of field
    async hget(key: string, field: string): Promise<string | null> {
        const res = await this.client.exec("HGET", key, field);
        if (res == null) {
            return null;
        }
        return res as string;
    }

    // return list of fields and their values stored in the hash
    async hgetAll(key: string): Promise<string[]> {
        const res = await this.client.exec("HGETALL", key);
        return res as string[];
    }

    // return the value at field after the increment operation.
    async hincrBy(key: string, field: string, value: number): Promise<number> {
        const res = await this.client.exec("HINCRBY", key, field, value.toFixed(0));
        return res as number;
    }

    // return the value at field after the increment operation.
    async hincrByFloat(key: string, field: string, value: number): Promise<string> {
        const res = await this.client.exec("HINCRBYFLOAT", key, field, value.toString());
        return res as string;
    }

    // return list of fields in the hash, or an empty list when key does not exist.
    async hkeys(key: string): Promise<string[]> {
        const res = await this.client.exec("HKEYS", key);
        return res as string[];
    }

    // return number of fields in the hash, or 0 when key does not exist.
    async hlen(key: string): Promise<number> {
        const res = await this.client.exec("HLEN", key);
        return res as number;
    }

    // return  list of values associated with the given fields, in the same order as they are requested.
    async hmget(key: string, filedList: string[]): Promise<string[]> {
        const res = await this.client.exec("HMGET", key, ...filedList);
        return res as string[];
    }

    async hrandField(key: string, count: number = 0, withValues: boolean = false): Promise<unknown> {
        const args = ["HRANDFIELD", key];
        if (count > 0) {
            args.push(count.toFixed(0));
        }
        if (withValues) {
            args.push("WITHVALUES");
        }
        const res = await this.client.exec(...args);
        return res;
    }

    async hscan(key: string, cursor: number, pattern: string = "", count: number = 0): Promise<unknown> {
        const args = ["HSCAN", key, cursor.toFixed(0)];
        if (pattern !== "") {
            args.push("MATCH", pattern);
        }
        if (count > 0) {
            args.push("COUNT", count.toFixed(0));
        }
        const res = await this.client.exec(...args);
        return res;
    }

    // return The number of fields that were added.
    async hset(key: string, fieldValueList: FieldValue[]): Promise<number> {
        const args = ["HSET", key];
        for (const fieldValue of fieldValueList) {
            args.push(fieldValue.field, fieldValue.value);
        }
        const res = await this.client.exec(...args);
        return res as number;
    }

    // return 1 if field is a new field in the hash and value was set,0 for not
    async hsetnx(key: string, field: string, value: string): Promise<number> {
        const res = await this.client.exec("HSETNX", key, field, value);
        return res as number;
    }

    // return the string length of the value associated with field, or zero when field is not present in the hash or key does not exist at all.
    async hstrlen(key: string, field: string): Promise<number> {
        const res = await this.client.exec("HSTRLEN", key, field);
        return res as number;
    }

    // return list of values in the hash, or an empty list when key does not exist.
    async hvals(key: string): Promise<string[]> {
        const res = await this.client.exec("HVALS", key);
        return res as string[];
    }
}